#ifndef FKOBJECT_H
#define FKOBJECT_H

#include "FKSystemObject.h"

#include <QSet>
#include <QLinkedList>
#include <QList>
#include <QStringList>

#include "fkobjects_global.h"

#include "FKEvent.h"

class FKAbstractObjectManager;
class FKRoom;

#define FK_OBJECT(derivedClass,parentClass) \
derivedClass : public parentClass{ \
    typedef void (derivedClass::*fkfPtr)(FKEvent*); \
    static class Events:public FKObject::Events{ \
        void initEvents(); \
        QMap<QString,derivedClass::fkfPtr> events; \
        QMap<QString,derivedClass::fkfPtr> actions; \
    public: \
        Events(){initEvents();} \
        QMap<QString,derivedClass::fkfPtr> eventList()const{return events;} \
        QMap<QString,derivedClass::fkfPtr> actionList()const{return actions;} \
    } _eventKeeper;\
    struct Servant;\
    struct CustomValues;\
    Servant* servant;\
    CustomValues* customValues;\
    void initLevel(){\
        customValues=new CustomValues();\
        derivedClass::customInitialization();\
    }\
protected: \
    bool initialization(){\
        if(parentClass::initialization()){\
            customValues=new CustomValues();\
            derivedClass::customInitialization();\
            return true;\
        }\
        return false;\
    }\
    void deinitialialization(){\
        derivedClass::customDeinitialization();\
        delete customValues;\
        parentClass::deinitialization();\
    }\
    virtual void executeEvent(FKEvent* ev){ \
        derivedClass::fkfPtr p=_eventKeeper.eventList().value(ev->subject(),0); \
        if(p)(this->*p)(ev); else parentClass::executeEvent(ev); \
    } \
    virtual void executeAction(FKEvent* ac){ \
        derivedClass::fkfPtr p=_eventKeeper.actionList().value(ac->subject(),0); \
        if(p)(this->*p)(ac); else parentClass::executeAction(ac); \
    }  \
public: \
    derivedClass(FKRoom* room=0,qint32 objectID=-1);\
    ~derivedClass();\
    virtual QStringList eventList()const{ \
        QStringList p(_eventKeeper.eventList().keys()); \
        p.append(parentClass::eventList()); \
        return p;} \
    virtual QStringList actionList()const{ \
        QStringList p(_eventKeeper.actionList().keys()); \
        p.append(parentClass::actionList()); \
        return p;} \
    virtual QStringList propList()const{ \
        QStringList p(_eventKeeper.propList()); \
        p.append(parentClass::propList()); \
        return p;} \
private:

#define FK_EVENTS(className) \
className::Events className::_eventKeeper=className::Events(); \
void className::Events::initEvents()

#define FK_CONSTRUCTOR \
    if(objectID==-1){\
        servant=new Servant;\
    }else{\
        servant=0;\
    }\
    customValues=0;\
    initLevels();

#define FK_DESTRUCTOR \
    if(servant)delete servant;\
    deinitObject();

#define FK_NO_SERVANT \
struct Servant{};

#define FK_NO_CUSTOM_VALUES \
    struct CustomValues{};

class FKOBJECTSSHARED_EXPORT FKObject:public FKSystemObject{
    Q_OBJECT
public:
    FKObject(FKRoom* room=0, qint32 objectID=-1);
    ~FKObject();
    void packObject(QDataStream &stream);
    static void deleteAll();
    qint32 getID()const;
    static FKObject* getObject(const qint32 &obj);
    static void setManager(FKAbstractObjectManager* manager);
    QString getClassName()const;
    void setRoom(FKRoom* room);
    qint32 getRoomID()const;
    FKRoom* room()const{return _room;}
    void applyUpdates();
    void refreshValues(FKObject* client);
    void refreshCommonValue(const QString& prop);
    void initObject();
    void deinitObject();
    void setActive(const bool& t);
public slots:
    void deleteFKObject();
    void processFKAction(FKEvent* action);
    void processFKEvent(FKEvent* event);
signals:
    void newEvent(FKEvent* event);
    void deletedFKObject(FKObject* obj);
protected:
    enum EventTarget{
        ToClientsAndServer,
        ToDistantClients,
        ToAllClients
    };
    void doEvent(FKObject* target,const QString &eventName,const QVariant &value, FKObject::EventTarget eventTarget=FKObject::ToClientsAndServer, qint32 user=-1);
    void doEvent(FKObject* target,const QString &eventName, FKObject::EventTarget eventTarget=FKObject::ToClientsAndServer, qint32 user=-1);
    void doEvent(FKObject* target,const QString &eventName,const QVariant &value, FKObject* eventTarget, qint32 user=-1);
    void doEvent(FKObject* target,const QString &eventName, FKObject* eventTarget, qint32 user=-1);
    void doEvent(const QString& errorText, FKObject* target);
    void updateProp(const QString &prop);
    virtual bool initialization();
    virtual void deinitialization(){}

    virtual void customInitialization(){}
    virtual void customDeinitialization(){}
private:
    void resetSharedVisorsCalculation();
    void releaseId();
    void submitId(const qint32 &id);
    static qint32 reserveId();

    FKRoom* _room;
    qint32 _objID;
    bool _initialized;
    static QHash<qint32,FKObject*> _refs;
    static qint32 _nextId;
    static QStack<qint32> _freed;
    static FKAbstractObjectManager* _manager;

//FKObject event system. This section creating for derived classes by FK_OBJECT macro

    //typedef void (FKObject::*fkfPtr)(FKEvent*);
protected:
    class Events{
        virtual void initEvents(){}
    protected:
        QStringList props;
    public:
        Events(){}
        virtual ~Events(){}
        QStringList propList()const{return props;}
    };
    virtual QStringList eventList()const{return QStringList();}
    virtual QStringList actionList()const{return QStringList();}
    virtual QStringList propList()const;
    virtual void executeEvent(FKEvent* ev){Q_UNUSED(ev)}
    virtual void executeAction(FKEvent* ac){Q_UNUSED(ac)}
    void initLevels();
private:
    virtual void initLevel(){}
    class Servant;
    Servant* servant;
//-----------------------------------------------------------------------------------

//vision section
public:
    bool setCommonWatching(FKObject* watched);
    bool setCustomWatching(FKObject* watched,const QString &prop=QString());
    bool setSharedWatching(FKObject* watched);
    bool setTotalWatching(FKObject* watched);
    bool stopCustomWatching(FKObject* watched,const QString &prop=QString());
    bool stopSharedWatching(FKObject* watched);
    void stopTotalWatching(FKObject* watched=0);

    bool allowControlBy(FKObject* actor,const QString &action=QString());
    bool forbidControlBy(FKObject* actor,const QString &action=QString());

    bool allowEmitBy(FKObject* user);
public slots:
    bool forbidEmitBy(FKObject* user);

protected:
    QSet<FKObject*> totalWatched()const;
    QSet<FKObject*> customVisors(const QString &prop)const;
    virtual QSet<FKObject*> sharedVisors();
    QSet<FKObject*> totalVisors()const;
private:
    virtual void visionChanged(){}
//---------------------------------------------------------------------------
};

class FKObject::Servant{
public:
    Servant():durty(true),active(true){}
    QSet<FKObject*> sharedWatchers;
    QSet<FKObject*> sharedWatcheds;
    QMap<QString, QSet<FKObject*> > watchers;
    QMap<QString, QSet<FKObject*> > watcheds;
    QMap<QString, bool> needUpdate;
    QMap<QString, QLinkedList<FKObject*> > actors;
    QMap<QString, QLinkedList<FKObject*> > controlled;
    QLinkedList<qint32> emitters;
    QSet<FKObject*> visors;
    bool durty;
    bool active;
};

#endif // FKOBJECT_H
